//////////////////////////////////////////////////////////////////////////
//
// This file is a part of UKROP project.
// Copyright (c) Vladimir Gumenuk
//
// PROJECT:         Xort Custom Debug Engine
// FILE:            Program.h : Declaration of the CProgram
// AUTHOR:          Vladimir Gumenuk
// DATE:            Dec-2012 -- Jan-2013
// MODIFICATIONS:
//
//////////////////////////////////////////////////////////////////////////

#pragma once
#include "XortDebugEngine_i.h"

extern const GUID __declspec(selectany) 
IID_IXortProgram = { 0xBE97D04A, 0x4E16, 0x11E2, { 0x99, 0xC2, 0x87, 0x0E, 0x61, 0x88, 0x70, 0x9B } };

class XRuntime;

enum XVMState
{
    XVM_Idle    = 0,
    XVM_Executing,
    XVM_DoSingleStep,
    XVM_AtBreakpoint,
    XVM_CauseBreak,
    XVM_Terminating,
};

// CProgram
class ATL_NO_VTABLE CProgram :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CProgram, &CLSID_Program>,
    public IDebugProgramNodeAttach2,
	public IDebugProgramNode2,
    public IDebugProgram2, 
    public IDebugThread2,
    public IXProgram,
    public XRuntime,
    public XRuntimeDebugEvents
{
public:
	CProgram();
	virtual ~CProgram();

DECLARE_REGISTRY_RESOURCEID(IDR_PROGRAM)

BEGIN_COM_MAP(CProgram)
	COM_INTERFACE_ENTRY(IDebugProgramNode2)
    COM_INTERFACE_ENTRY(IDebugProgram2) 
    COM_INTERFACE_ENTRY(IDebugThread2)
    COM_INTERFACE_ENTRY(IDebugProgramNodeAttach2)
    COM_INTERFACE_ENTRY(IXProgram)
    COM_INTERFACE_ENTRY_IID(IID_IXortProgram, CProgram)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

IMPLEMENT_CREATE( CProgram, IDebugProgram2 );

	HRESULT FinalConstruct();
	void FinalRelease();

    HRESULT EngineAttach( IDebugProgram2* pMDMProgram, IDebugEngine2 *pEngine, IDebugProcess2 *pProcess, IDebugEventCallback2* pCallback, DWORD dwReason );

public:
    //////////////////////////////////////////////////////////// 
    // IDebugProgramNode2 
    STDMETHOD(GetProgramName)(BSTR* pbstrProgramName); 
    STDMETHOD(GetHostName)(DWORD dwHostNameType, BSTR* pbstrHostName); 
    STDMETHOD(GetHostPid)(AD_PROCESS_ID *pHostProcessId); 
    STDMETHOD(GetHostMachineName_V7)(BSTR* pbstrHostMachineName); 
    STDMETHOD(Attach_V7)( IDebugProgram2* pMDMProgram, IDebugEventCallback2* pCallback, DWORD dwReason); 
    STDMETHOD(GetEngineInfo)(BSTR* pbstrEngine, GUID* pguidEngine); 
    STDMETHOD(DetachDebugger_V7)();

    //////////////////////////////////////////////////////////// 
    // IDebugProgram2 
    STDMETHOD(EnumThreads)( IEnumDebugThreads2** ppEnum ); 
    STDMETHOD(GetName)( BSTR* pbstrName ); 
    STDMETHOD(GetProcess)( IDebugProcess2** ppProcess ); 
    STDMETHOD(Terminate)(); 
    STDMETHOD(Attach)( IDebugEventCallback2* pCallback ); 
    STDMETHOD(Detach)(); 
    STDMETHOD(GetProgramId)( GUID* pguidProgramId ); 
    STDMETHOD(GetDebugProperty)( IDebugProperty2** ppProperty ); 
    STDMETHOD(Execute)(); 
    STDMETHOD(Continue)( IDebugThread2 *pThread ); 
    STDMETHOD(Step)( IDebugThread2 *pThread, STEPKIND sk, STEPUNIT step ); 
    STDMETHOD(CauseBreak)(); 
    //GetEngineInfo already defined for IDebugProgramNode2 
    STDMETHOD(EnumCodeContexts)( IDebugDocumentPosition2* pDocPos, IEnumDebugCodeContexts2** ppEnum ); 
    STDMETHOD(GetMemoryBytes)( IDebugMemoryBytes2** ppMemoryBytes ); 
    STDMETHOD(GetDisassemblyStream)( DISASSEMBLY_STREAM_SCOPE dwScope, IDebugCodeContext2* pCodeContext, IDebugDisassemblyStream2** ppDisassemblyStream ); 
    STDMETHOD(EnumModules)( IEnumDebugModules2** ppEnum ); 
    STDMETHOD(GetENCUpdate)( IDebugENCUpdate** ppUpdate ); 
    STDMETHOD(EnumCodePaths)( LPCOLESTR pszHint, IDebugCodeContext2* pStart, IDebugStackFrame2* pFrame, BOOL fSource, IEnumCodePaths2** ppEnum, IDebugCodeContext2** ppSafety ); 
    STDMETHOD(WriteDump)(DUMPTYPE DumpType, LPCOLESTR pszCrashDumpUrl); 
    STDMETHOD(CanDetach)(); 

    //////////////////////////////////////////////////////////// 
    // IDebugThread2 
    STDMETHOD(EnumFrameInfo)( FRAMEINFO_FLAGS dwFieldSpec, UINT nRadix, IEnumDebugFrameInfo2 **ppEnum ); 
    // GetName already defined for IDebugProgram 
    STDMETHOD(SetThreadName)( LPCOLESTR pszName ); 
    STDMETHOD(GetProgram)( IDebugProgram2** ppProgram ); 
    STDMETHOD(CanSetNextStatement)( IDebugStackFrame2 *pStackFrame, IDebugCodeContext2 *pCodeContext ); 
    STDMETHOD(SetNextStatement)( IDebugStackFrame2 *pStackFrame, IDebugCodeContext2 *pCodeContext ); 
    STDMETHOD(GetThreadId)( DWORD* pdwThreadId ); 
    STDMETHOD(Suspend)( DWORD *pdwSuspendCount ); 
    STDMETHOD(Resume)( DWORD *pdwSuspendCount ); 
    STDMETHOD(GetThreadProperties)( THREADPROPERTY_FIELDS dwFields, THREADPROPERTIES *ptp ); 
    STDMETHOD(GetLogicalThread)( IDebugStackFrame2 *pStackFrame, IDebugLogicalThread2 **ppLogicalThread ); 

    //////////////////////////////////////////////////////////////////////////
    // IDebugProgramNodeAttach2
    STDMETHOD(OnAttach)( __RPC__in REFGUID guidProgramId );

    //////////////////////////////////////////////////////////////////////////
    // 
    STDMETHOD(Load)( LPCOLESTR lpszXRTFile );
    STDMETHOD(CreatePendingBreakpoint)( IDebugBreakpointRequest2 *pBPRequest, IDebugPendingBreakpoint2** ppPendingBP ); 
    STDMETHOD(FindSourceFile)( LPCOLESTR lpszFileName, IDebugDocument2 **ppDoc );
    STDMETHOD(GetGlobalSymbolTable)( IUnknown **ppOut );
    STDMETHOD(CanStop)( BOOL bStop );
    STDMETHOD(GetCurrentIP)( DWORD *ppOut );
    STDMETHOD(GetDocument)( IDebugDocument2 **ppOut );
    STDMETHOD(SignalEvent)();
    STDMETHOD(GetXRuntime)( IUnknown **ppOut ); // actually XRuntime pointer with noAddRef is returned

    //////////////////////////////////////////////////////////////////////////
    // XVM debug interface, all comes from internal thread
    virtual int onEnterSystemFrame( XRuntime *pRT, SymbolRef CurrentSymbol );
    virtual int onLeaveSystemFrame( XRuntime *pRT, SymbolRef CurrentSymbol );
    virtual int onEnterFrame( XRuntime *pRT );
    virtual int onLeaveFrame( XRuntime *pRT );
    virtual int beforeExecPrimitive( XRuntime *pRT, const DecoderData& primInfo );
    virtual int afterExecPrimitive( XRuntime *pRT, const DecoderData& primInfo, int iLastError );
    virtual int beforeExecBuildinProc( XRuntime *pRT );
    virtual int afterExecBuildinProc( XRuntime *pRT, int iLastError );
    virtual int beforeExecNativeProc( XRuntime *pRT );
    virtual int afterExecNativeProc( XRuntime *pRT, int iLastError );
    virtual int internalXVMError( XRuntime *pRT, int iError );
    virtual void outputDebugMessage( LPCSTR lpszMessage );

private:
    static DWORD __stdcall RuntimeEntryStatic( LPVOID pParam );
    DWORD RuntimeRun();
    void LoadProgram();

    inline void     setState( XVMState newState ) { m_eState = newState; }
    inline XVMState getState() const { return m_eState; }
    
    // builds debug stack frame info for the newly entered procedure
    HRESULT BuildStackFrame( CFrameInfo **ppOut );

    HRESULT UpdateStackFrame( IXStackFrame *pStkFrame );

    GUID                                m_guidProgId;          // assigned by SDM after attach

    CComPtr<IDebugProgramPublisher2>    m_srpProgramPublisher; // actually created by the VS when it starts the program
    CComPtr<IDebugDocument2>            m_spSourceFile;        // source file

    string                              m_csFullCmdLine;
    string                              m_csXrtFileName;    // source file name
    int                                 m_iArgc;
    char                              **m_pArgv;
    SYMBOL_LIST                         m_listGlobal;       // global symbol table
    STRING_ARRAY                        m_litPool;          // literal pool
    symbolref                           m_pCurProc;         // the symbol of procedure, that is currently executed by XVM
    SYMBOL_LIST::iterator               m_stackIter;        // iterator to the stack transition symbols for current procedure
    CDebugStack                         m_dbgStack;         // this stack will hold the information about XVM stack transitions
    char                                m_szLastInstr[64];  // last instruction executed
    HANDLE                              m_hEvent;           // synchronization event
    HANDLE                              m_hRuntimeThread;   // XVM thread
    DWORD                               m_dwThreadID;

    XVMState                            m_eState;           // current program state
    STEPKIND                            m_NextStepKind;     // into || over || out - for next procedure
    STEPUNIT                            m_StepUnit;         // statement || line || instruction
    BOOL                                m_bStopFlag;   
    CComCriticalSection                 m_CS;

};

OBJECT_ENTRY_AUTO(__uuidof(Program), CProgram)
